iT邦幫忙

2022 iThome 鐵人賽

DAY 26
0

昨天看完useContext,今天要來看讓我非常興奮的useReducer!之所以興奮是因為,之前第一次看到redux時,覺得超複雜的,什麼reducer、store、dispatch...怎麼看不太懂。今天的部分你只要學完了,未來要使用redux時,應該會覺得「咦,怎麼這些語法這麼熟悉?」!讓我們開始吧:

讓我們來看看useReducer在型別宣告檔內的說明吧,你應該會更清楚什麼時候要用它:

useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values. It also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.

簡單來說,當你有複雜的狀態需要管理時,就可以使用useReducer來管理,他甚至能為你帶來效能上的改善,因為平常使用useState時,我們都會將callback傳入子層來改變state,這點有可能造成不必要的渲染。而使用useReducer後,我們只要將dispatch這方法傳下去子層就好。

要使用useReducer,我們需要一些起手式:

  1. initialState 起始值
  2. ActionTypes 改變state的特定形式(動作形式?我亂翻的)

//覺得reducer的相關用詞怎麼翻都不太理想

我們還是用超超超基本的計數器來當作範例的,先讓我們做個initialState起始值

useReducer的initialState

const initialState = {count: 87}

我們等等就根據這個state來進行變動,看是要加多少/減多少,再呈現在UI上。

接著創造actionTypes,我們要告訴React,之後只有哪些類型,可以對對應的state進行操作:

useReducer的actionTypes

type ACTIONTYPES =
  | { type: "increment"; payload: number }
  | { type: "decremenet"; payload: number }

我們這邊用型別別名先宣告了兩個型別,特別的是型別內部的type是明確的字串值,不是"increment"就是"decremenet",payload則是之後我們想要讓他加多少/減多少的數量,型別為數字。
如果你不習慣這樣的宣告形式的話...只能請你習慣了,這是使用reducer必須的作法。

再來,我們終於要製作reducer了!

useReducer的...reducer

const countReducer = (state: typeof initialState, action: ACTIONTYPES) => {
  switch (action.type) {
      //要加多少
    case "increment":
      return {
        ...state,
        count: state.count + action.payload
      }
      //要減多少
    case "decremenet":
      return {
        ...state,
        count: state.count - action.payload
      }
      //如果不是正確的type,那就拋出錯誤吧
    default:
      throw new Error("noooooo")
  }
}

我們宣告出一個函式:countReducer,這函式有兩個參數:起始值(等等會放入initialState)、action物件,並根據action物件內的type來決定要對state執行的動作。
參數state的型別,我們不另外宣告type或interface的話,就跟昨天一樣,用TypeScript的typeof關鍵字,引用initialState的型別{count: number},而action的型別,當然是剛剛宣告過的ACTIONTYPES囉,編輯器就會很讚的跟我們說這個action.type有什麼能用了!

像我們在打case ""的時候,編輯器就直接提供我們兩個選項"increment""decremenet",完全不用另外翻找了呢。

扯了這麼多,剛剛都只是前置作業,我們還沒在元件內使用這些code呢,接下來請輸入這些code:

實際使用useReducer

const ReducerComponent = () => {
    //前置作業這麼長,只為下面這行
  const [state, dispatch] = useReducer(countReducer, initialState)

  return (
    <>
      <span>Current count is {state.count}</span>
      <button
        onClick={() => {
          dispatch({ type: "increment", payload: 200 })
        }}
      >
        Increment 200
      </button>
      <button
        onClick={() => {
          dispatch({ type: "decremenet", payload: 50 })
        }}
      >
        Decrement 50
      </button>
    </>
  )
}

//最後再自己渲染到App.tsx內就好了

我們終於用了useReducer了,他的參數有哪些呢?你可以在useReducer字樣上進行hover了解細節,簡單來說第一個就是reducer啦,處理後續state的變更邏輯,第二個參數則是初始狀態(你會發現我好像一直在講重複的事情,但這邊的確得這樣)。取到的值,跟useState有些類似,解構後的第一個值為state(一個有count的物件),第二個值是dispatch,用來發送action的函式。

渲染後,我們便能在螢幕上看到 Current count is 87
後面兩個按鈕,我們為他放入一個onClick的callback,分別發出(dispatch)一個action,dispatch的引數就是個action物件,所以當我們在輸入dispatch({action:"",payload:50})的時候,編輯器又自動推薦我們可以選擇的action了,不要以為推薦這個沒什麼,當你的程式碼開始變多、需要一直跳來跳去時,這邊的提示就顯得相當關鍵。

VSCode溫馨的推薦字串

最後你就會發現,我們能順利的透過按鈕更改state了!!當然這邊的state一點都不複雜啦,可是實際使用時,有reducer能先定義更改邏輯時,會發現程式更好控管了。

小結

TypeScript用於useReducer時能大大節省我們後續查找類型的時間,只要在reducer階段宣告好action的型別,以及initialState的型別,後續在進行每一步時,編輯器都會提示我們應該要寫些什麼,超方便的。

其實一開始在選這主題時,我是真的不覺得TypeScript好用,因為你要多寫太多的code了,實在很繁瑣,但隨著這幾天用React的各種hook當作範例後,就發現多寫的那些code,能讓開發者在後續開發上省下不少麻煩&時間,覺得花這些學習成本很值得!


上一篇
第25天!TypeScript 與 useContext!
下一篇
第27天!TypeScript與customHook!
系列文
你也對開始使用typescript感到無力嗎?我也是 - 30天初探typescript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
yojijun
iT邦新手 4 級 ‧ 2022-10-11 15:00:01

倒數倒數!(打鼓

Lin Chen iT邦新手 4 級 ‧ 2022-10-11 23:29:08 檢舉

最後四天要寫!!!

我要留言

立即登入留言